import ipaddress
import socket
from .models import Host
from celery import shared_task
from .utils.snmp_collector import SNMPCollector
from .utils.ssh_collector import SSHCollector
from .utils.ipmi_collector import IPMICollector
from datetime import datetime
from pysnmp.hlapi import *
import logging

logger = logging.getLogger(__name__)

def discover_hosts(network):
    """
    发现网段内的主机
    """
    try:
        discovered_hosts = []
        # 解析网段
        network = ipaddress.ip_network(network)
        
        for ip in network.hosts():
            ip_str = str(ip)
            # 检查是否已存在
            if not Host.objects.filter(ip_address=ip_str).exists():
                # 尝试SNMP连接
                client = SNMPClient(
                    host=ip_str,
                    community='public',  # 使用默认community
                    port=161,
                    version='2c'
                )
                
                if client.test_connection():
                    # 创建新主机记录
                    host = Host.objects.create(
                        hostname=f'Host-{ip_str}',
                        ip_address=ip_str,
                        monitor_type='snmp',
                        status='enabled',
                        credentials={'community': 'public'},
                        snmp_port=161,
                        snmp_version='2c'
                    )
                    discovered_hosts.append(host)
                    
                    # 立即执行一次监控
                    monitor_host(host.id)
        
        return discovered_hosts
        
    except Exception as e:
        logger.error(f"发现主机失败: {str(e)}")
        raise Exception(f"发现主机失败: {str(e)}")

def check_port(ip, port):
    """检查指定IP的端口是否开放"""
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(1)
    try:
        result = sock.connect_ex((ip, port))
        return result == 0
    except:
        return False
    finally:
        sock.close()

@shared_task
def collect_host_data(host_id):
    """收集单个主机的所有监控数据"""
    try:
        host = Host.objects.get(id=host_id)
        if not host.status == 'enabled':
            return f"主机 {host.hostname} 未启用，跳过数据收集"

        if host.monitor_type == 'snmp':
            collector = SNMPCollector(host)
            collector.collect_all()
        elif host.monitor_type == 'ssh':
            collector = SSHCollector(host)
            collector.collect_all()
        elif host.monitor_type == 'ipmi':
            collector = IPMICollector(host)
            collector.collect_all()
        
        host.last_check = datetime.now()
        host.save()
        
        return f"成功收集主机 {host.hostname} 的监控数据"
    except Host.DoesNotExist:
        return f"主机ID {host_id} 不存在"
    except Exception as e:
        return f"收集主机ID {host_id} 数据失败: {str(e)}"

@shared_task
def collect_all_hosts_data():
    """收集所有启用主机的监控数据"""
    try:
        hosts = Host.objects.filter(status='enabled')
        results = []
        
        for host in hosts:
            # 为每个主机异步启动一个数据收集任务
            results.append(collect_host_data.delay(host.id))
        
        return f"已启动 {len(results)} 个数据收集任务"
    except Exception as e:
        return f"启动数据收集任务失败: {str(e)}"

class SNMPClient:
    def __init__(self, host, community='public', port=161, version='2c'):
        self.host = host
        self.community = community
        self.port = port
        self.version = version

    def get_snmp_data(self, oid):
        """获取单个 OID 的数据"""
        try:
            if self.version == '2c':
                iterator = getNext(
                    SnmpEngine(),
                    CommunityData(self.community, mpModel=1),
                    UdpTransportTarget((self.host, self.port)),
                    ContextData(),
                    ObjectType(ObjectIdentity(oid))
                )
                errorIndication, errorStatus, errorIndex, varBinds = next(iterator)

                if errorIndication:
                    raise Exception(f'SNMP错误: {errorIndication}')
                elif errorStatus:
                    raise Exception(f'SNMP错误: {errorStatus}')
                
                for varBind in varBinds:
                    return varBind[1]
            return None
        except Exception as e:
            raise Exception(f'SNMP获取数据失败: {str(e)}')

    def get_system_metrics(self):
        """获取系统指标"""
        try:
            # CPU 负载 OID (1分钟负载)
            CPU_LOAD_OID = '1.3.6.1.4.1.2021.10.1.3.1'
            # 内存使用 OID
            TOTAL_MEMORY_OID = '1.3.6.1.4.1.2021.4.5.0'  # Total RAM
            FREE_MEMORY_OID = '1.3.6.1.4.1.2021.4.6.0'   # Free RAM
            # 磁盘使用 OID
            DISK_TOTAL_OID = '1.3.6.1.4.1.2021.9.1.6.1'  # Total disk space
            DISK_USED_OID = '1.3.6.1.4.1.2021.9.1.8.1'   # Used disk space

            # 获取CPU负载
            cpu_load = float(self.get_snmp_data(CPU_LOAD_OID) or 0)
            cpu_usage = min(cpu_load * 100, 100)  # 转换为百分比

            # 获取内存使用情况
            total_memory = int(self.get_snmp_data(TOTAL_MEMORY_OID) or 0)
            free_memory = int(self.get_snmp_data(FREE_MEMORY_OID) or 0)
            memory_usage = 0
            if total_memory > 0:
                memory_usage = ((total_memory - free_memory) / total_memory) * 100

            # 获取磁盘使用情况
            disk_total = int(self.get_snmp_data(DISK_TOTAL_OID) or 0)
            disk_used = int(self.get_snmp_data(DISK_USED_OID) or 0)
            disk_usage = 0
            if disk_total > 0:
                disk_usage = (disk_used / disk_total) * 100

            return {
                'cpu_usage': round(cpu_usage, 2),
                'memory_usage': round(memory_usage, 2),
                'disk_usage': round(disk_usage, 2),
                'timestamp': datetime.now()
            }
        except Exception as e:
            raise Exception(f'获取系统指标失败: {str(e)}')

    def test_connection(self):
        """测试SNMP连接"""
        try:
            # 使用系统描述OID测试连接
            SYSTEM_DESCRIPTION_OID = '1.3.6.1.2.1.1.1.0'
            result = self.get_snmp_data(SYSTEM_DESCRIPTION_OID)
            return result is not None
        except Exception:
            return False 

def monitor_host(host_id):
    """
    监控单个主机
    """
    try:
        host = Host.objects.get(id=host_id)
        
        # 检查主机是否启用
        if host.status != 'enabled':
            return False
            
        # 根据监控类型执行不同的监控方法
        if host.monitor_type == 'snmp':
            success = host.update_snmp_metrics()
            return {
                'success': success,
                'metrics': {
                    'cpu_usage': host.snmp_cpu_usage,
                    'memory_usage': host.snmp_memory_usage,
                    'disk_usage': host.snmp_disk_usage,
                    'timestamp': host.snmp_last_update
                } if success else None
            }
        # ... 其他监控类型的处理 ...
        
    except Host.DoesNotExist:
        logger.error(f"主机不存在: {host_id}")
        return False
    except Exception as e:
        logger.error(f"监控主机失败: {str(e)}")
        return False 